package cn.com.hhrcw.es.service.impl;

import cn.com.hhrcw.es.service.IElasticsearchService;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.jeecg.common.es.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

/**
 * Elasticsearch 搜索服务
 *
 * @author lixingxing
 * @since 2020-08-12
 */
@Service
@Slf4j
public class ElasticsearchServiceImpl implements IElasticsearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @SneakyThrows
    @Override
    @Async
    public <T> void add(T... documents) {
        if (ArrayUtil.isEmpty(documents)) {
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        for (T document : documents) {
            String id = null;
            Document docu = document.getClass().getAnnotation(Document.class);
            for (Field field : document.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Id.class)) {
                    id = ReflectUtil.getFieldValue(document, field).toString();
                    break;
                }
            }
            if (StrUtil.isBlank(id)) {
                throw new NullPointerException("没有ID");
            }
            DocWriteRequest<?> request;
            if (!this.exists(docu.index(), docu.type(), id)) {
                request = new IndexRequest(docu.index(), docu.type()).id(id).source(JSON.toJSONString(document), XContentType.JSON);
            } else {
                request = new UpdateRequest(docu.index(), docu.type(), id).doc(JSON.toJSONString(document), XContentType.JSON);
            }
            bulkRequest.add(request);
        }
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    @Override
    @SneakyThrows
    @Async
    public <T> IPage<T> searchForPage(SearchRequest searchRequest, IPage<T> pageable, Class<T> clz) {
        Document document = clz.getAnnotation(Document.class);
        searchRequest.indices(document.index()).types(document.type());
        long from = (pageable.getCurrent() - 1) * pageable.getSize();
        searchRequest.source().from((int) from).size((int) pageable.getSize());
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        List<T> list = Lists.newArrayList();
        response.getHits().forEach(his -> {
            T t = JSON.parseObject(his.getSourceAsString(), clz);
            list.add(t);
        });
        pageable.setTotal(response.getHits().totalHits);
        pageable.setRecords(list);
        return pageable;
    }

    @Override
    @SneakyThrows
    @Async
    public <T> List<T> search(SearchRequest searchRequest, Class<T> clz) {
        Document document = clz.getAnnotation(Document.class);
        searchRequest.indices(document.index()).types(document.type());
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        List<T> list = Lists.newArrayList();
        response.getHits().forEach(his -> {
            T t = JSON.parseObject(his.getSourceAsString(), clz);
            list.add(t);
        });
        return list;
    }

    @Override
    @SneakyThrows
    @Async
    public <T> void delete(Class<T> clz, String... id) {
        Document document = clz.getAnnotation(Document.class);
        GetIndexRequest indices = new GetIndexRequest().indices(document.index());
        boolean exists = restHighLevelClient.indices().exists(indices, RequestOptions.DEFAULT);
        if (ArrayUtil.isEmpty(id) && exists) {
            DeleteIndexRequest request = new DeleteIndexRequest(document.index());
            restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            return;
        }
        if(!exists){
            log.debug("索引不存在");
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        for (String s : id) {
            DeleteRequest request = new DeleteRequest(document.index(), document.type(), s);
            bulkRequest.add(request);
        }
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    public boolean exists(String index, String type, String id) throws IOException {
        GetRequest request = new GetRequest(index, type, id);
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");
        return restHighLevelClient.exists(request, RequestOptions.DEFAULT);

    }
}
